//! Configuration module for the Rust GraphQL application
//! 
//! This module provides configuration management using environment variables
//! and configuration files.

use serde::{Deserialize, Serialize};
use std::env;
use std::time::Duration;

/// Main configuration structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    /// Database connection URL
    pub database_url: String,
    /// Server host address
    pub host: String,
    /// Server port
    pub port: u16,
    /// Database configuration
    pub database: DatabaseConfig,
    /// GraphQL configuration
    pub graphql: GraphQLConfig,
}

/// Database-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    /// Maximum number of connections in the pool
    pub max_connections: u32,
    /// Minimum number of connections in the pool
    pub min_connections: u32,
    /// Connection timeout in seconds
    pub connect_timeout_secs: u64,
    /// Idle timeout in seconds
    pub idle_timeout_secs: u64,
}

/// GraphQL-specific configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphQLConfig {
    /// Maximum query depth allowed
    pub max_depth: usize,
    /// Maximum query complexity allowed
    pub max_complexity: usize,
    /// Enable GraphQL playground in production
    pub enable_playground: bool,
    /// Enable introspection queries
    pub enable_introspection: bool,
}

impl Config {
    /// Load configuration from environment variables
    pub fn from_env() -> anyhow::Result<Self> {
        let database_url = env::var("DATABASE_URL")
            .unwrap_or_else(|_| "postgresql://moran:321525@localhost:5432/rust_graphql".to_string());
        
        let host = env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string());
        let port = env::var("PORT")
            .unwrap_or_else(|_| "8000".to_string())
            .parse()
            .unwrap_or(8000);
        
        let max_connections = env::var("DB_MAX_CONNECTIONS")
            .unwrap_or_else(|_| "100".to_string())
            .parse()
            .unwrap_or(100);
        
        let min_connections = env::var("DB_MIN_CONNECTIONS")
            .unwrap_or_else(|_| "5".to_string())
            .parse()
            .unwrap_or(5);
        
        let connect_timeout_secs = env::var("DB_CONNECT_TIMEOUT")
            .unwrap_or_else(|_| "30".to_string())
            .parse()
            .unwrap_or(30);
        
        let idle_timeout_secs = env::var("DB_IDLE_TIMEOUT")
            .unwrap_or_else(|_| "600".to_string())
            .parse()
            .unwrap_or(600);
        
        let max_depth = env::var("GRAPHQL_MAX_DEPTH")
            .unwrap_or_else(|_| "10".to_string())
            .parse()
            .unwrap_or(10);
        
        let max_complexity = env::var("GRAPHQL_MAX_COMPLEXITY")
            .unwrap_or_else(|_| "1000".to_string())
            .parse()
            .unwrap_or(1000);
        
        let enable_playground = env::var("GRAPHQL_ENABLE_PLAYGROUND")
            .unwrap_or_else(|_| "true".to_string())
            .parse()
            .unwrap_or(true);
        
        let enable_introspection = env::var("GRAPHQL_ENABLE_INTROSPECTION")
            .unwrap_or_else(|_| "true".to_string())
            .parse()
            .unwrap_or(true);
        
        Ok(Config {
            database_url,
            host,
            port,
            database: DatabaseConfig {
                max_connections,
                min_connections,
                connect_timeout_secs,
                idle_timeout_secs,
            },
            graphql: GraphQLConfig {
                max_depth,
                max_complexity,
                enable_playground,
                enable_introspection,
            },
        })
    }
    
    /// Get database connection timeout as Duration
    pub fn connect_timeout(&self) -> Duration {
        Duration::from_secs(self.database.connect_timeout_secs)
    }
    
    /// Get database idle timeout as Duration
    pub fn idle_timeout(&self) -> Duration {
        Duration::from_secs(self.database.idle_timeout_secs)
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            database_url: "postgresql://postgres:password@localhost:5432/rust_graphql".to_string(),
            host: "0.0.0.0".to_string(),
            port: 8000,
            database: DatabaseConfig::default(),
            graphql: GraphQLConfig::default(),
        }
    }
}

impl Default for DatabaseConfig {
    fn default() -> Self {
        Self {
            max_connections: 100,
            min_connections: 5,
            connect_timeout_secs: 30,
            idle_timeout_secs: 600,
        }
    }
}

impl Default for GraphQLConfig {
    fn default() -> Self {
        Self {
            max_depth: 10,
            max_complexity: 1000,
            enable_playground: true,
            enable_introspection: true,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::env;
    
    #[test]
    fn test_config_default() {
        let config = Config::default();
        assert_eq!(config.host, "0.0.0.0");
        assert_eq!(config.port, 8000);
        assert_eq!(config.database.max_connections, 100);
    }
    
    #[test]
    fn test_config_from_env() {
        env::set_var("DATABASE_URL", "postgresql://test:test@localhost:5432/test");
        env::set_var("HOST", "127.0.0.1");
        env::set_var("PORT", "3000");
        
        let config = Config::from_env().unwrap();
        assert_eq!(config.database_url, "postgresql://test:test@localhost:5432/test");
        assert_eq!(config.host, "127.0.0.1");
        assert_eq!(config.port, 3000);
        
        // Clean up
        env::remove_var("DATABASE_URL");
        env::remove_var("HOST");
        env::remove_var("PORT");
    }
    
    #[test]
    fn test_timeout_conversion() {
        let config = Config::default();
        assert_eq!(config.connect_timeout(), Duration::from_secs(30));
        assert_eq!(config.idle_timeout(), Duration::from_secs(600));
    }
}